use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering};
macro_rules! slist_null {
    () => {
        None
    };
}
macro_rules! custom_assert {
    ($ expr : expr) => {
        num_assert.fetch_add(1, Ordering::SeqCst);
        if !$expr {
            panic!("Assertion failed: {}", stringify!($expr));
        }
    };
}
type SListValue = *mut c_void;
#[derive(Debug, Clone)]
struct SListEntry {}
#[repr(C)]
struct SListIterator {
    prev_next: *mut *mut SListEntry,
    current: *mut SListEntry,
}
#[derive(Clone, Copy)]
type UnitTestFunction = fn();
type SListCompareFunc = fn(SListValue, SListValue) -> i32;
type SListEqualFunc = fn(SListValue, SListValue) -> i32;
static num_assert: AtomicUsize = AtomicUsize::new(0);
static mut variable1: i32 = 50;
static mut variable2: i32 = 0;
static mut variable3: i32 = 0;
static mut variable4: i32 = 0;
static tests: &[UnitTestFunction] = &[
    test_slist_append,
    test_slist_prepend,
    test_slist_free,
    test_slist_next,
    test_slist_nth_entry,
    test_slist_nth_data,
    test_slist_length,
    test_slist_remove_entry,
    test_slist_remove_data,
    test_slist_sort,
    test_slist_find_data,
    test_slist_to_array,
    test_slist_iterate,
    test_slist_iterate_bad_remove,
    test_slist_iterate_bad_remove,
];
fn generate_list() -> *mut SListEntry {
    let mut list: *mut SListEntry = ptr::null_mut();
    assert!(!slist_append(&mut list, &variable1).is_null());
    assert!(!slist_append(&mut list, &variable2).is_null());
    assert!(!slist_append(&mut list, &variable3).is_null());
    assert!(!slist_append(&mut list, &variable4).is_null());
    list
}
#[test]
fn test_slist_append() {
    unsafe {
        assert!(slist_append(&mut LIST, &VARIABLE1).is_some());
        assert!(slist_append(&mut LIST, &VARIABLE2).is_some());
        assert!(slist_append(&mut LIST, &VARIABLE3).is_some());
        assert!(slist_append(&mut LIST, &VARIABLE4).is_some());
        assert_eq!(slist_length(LIST), 4);
        assert!(slist_nth_data(LIST, 0) == Some(&VARIABLE1));
        assert!(slist_nth_data(LIST, 1) == Some(&VARIABLE2));
        assert!(slist_nth_data(LIST, 2) == Some(&VARIABLE3));
        assert!(slist_nth_data(LIST, 3) == Some(&VARIABLE4));
        alloc_test_set_limit(0);
        assert_eq!(slist_length(LIST), 4);
        assert!(slist_append(&mut LIST, &VARIABLE1).is_none());
        assert_eq!(slist_length(LIST), 4);
        slist_free(LIST);
    }
}
#[test]
fn test_slist_prepend() {
    let mut list: *mut SListEntry = std::ptr::null_mut();
    assert!(!slist_prepend(&mut list, &mut variable1).is_null());
    assert!(!slist_prepend(&mut list, &mut variable2).is_null());
    assert!(!slist_prepend(&mut list, &mut variable3).is_null());
    assert!(!slist_prepend(&mut list, &mut variable4).is_null());
    assert!(slist_nth_data(list, 0) == &variable4);
    assert!(slist_nth_data(list, 1) == &variable3);
    assert!(slist_nth_data(list, 2) == &variable2);
    assert!(slist_nth_data(list, 3) == &variable1);
    alloc_test_set_limit(0);
    assert_eq!(slist_length(list), 4);
    assert!(slist_prepend(&mut list, &mut variable1).is_null());
    assert_eq!(slist_length(list), 4);
    slist_free(list);
}
#[test]
fn test_slist_free() {
    let mut list: *mut SListEntry;
    list = generate_list();
    unsafe {
        slist_free(list);
        slist_free(std::ptr::null_mut());
    }
}
#[test]
fn test_slist_next() {
    unsafe {
        let mut list = generate_list();
        let mut rover = list;
        assert_eq!(slist_data(rover), &variable1);
        rover = slist_next(rover);
        assert_eq!(slist_data(rover), &variable2);
        rover = slist_next(rover);
        assert_eq!(slist_data(rover), &variable3);
        rover = slist_next(rover);
        assert_eq!(slist_data(rover), &variable4);
        rover = slist_next(rover);
        assert!(rover.is_null());
        slist_free(list);
    }
}
#[test]
fn test_slist_nth_entry() {
    unsafe {
        let list: *mut SListEntry = generate_list();
        let mut entry: *mut SListEntry;
        entry = slist_nth_entry(list, 0);
        assert!(slist_data(entry) == &variable1);
        entry = slist_nth_entry(list, 1);
        assert!(slist_data(entry) == &variable2);
        entry = slist_nth_entry(list, 2);
        assert!(slist_data(entry) == &variable3);
        entry = slist_nth_entry(list, 3);
        assert!(slist_data(entry) == &variable4);
        entry = slist_nth_entry(list, 4);
        assert!(entry.is_null());
        entry = slist_nth_entry(list, 400);
        assert!(entry.is_null());
        slist_free(list);
    }
}
#[test]
fn test_slist_nth_data() {
    let list: *mut SListEntry;
    list = generate_list();
    assert_eq!(slist_nth_data(list, 0), &variable1 as *const _ as *mut _);
    assert_eq!(slist_nth_data(list, 1), &variable2 as *const _ as *mut _);
    assert_eq!(slist_nth_data(list, 2), &variable3 as *const _ as *mut _);
    assert_eq!(slist_nth_data(list, 3), &variable4 as *const _ as *mut _);
    assert_eq!(slist_nth_data(list, 4), std::ptr::null_mut());
    assert_eq!(slist_nth_data(list, 400), std::ptr::null_mut());
    slist_free(list);
}
#[test]
fn test_slist_length() {
    unsafe {
        let mut list: *mut SListEntry;
        list = generate_list();
        assert!(slist_length(list) == 4);
        slist_prepend(&mut list, &mut variable1);
        assert!(slist_length(list) == 5);
        assert!(slist_length(ptr::null_mut()) == 0);
        slist_free(list);
    }
}
#[test]
fn test_slist_remove_entry() {
    let mut empty_list: Option<Box<SListEntry>> = None;
    let mut list = generate_list();
    let mut entry;
    entry = slist_nth_entry(&mut list, 2);
    assert!(slist_remove_entry(&mut list, entry) != 0);
    assert!(slist_length(&list) == 3);
    entry = slist_nth_entry(&mut list, 0);
    assert!(slist_remove_entry(&mut list, entry) != 0);
    assert!(slist_length(&list) == 2);
    assert!(slist_remove_entry(&mut list, entry) == 0);
    assert!(slist_remove_entry(&mut list, None) == 0);
    assert!(slist_remove_entry(&mut empty_list, None) == 0);
    slist_free(list);
}
#[test]
fn test_slist_remove_data() {
    let entries = vec![89, 4, 23, 42, 4, 16, 15, 4, 8, 99, 50, 30, 4];
    let num_entries = entries.len();
    let mut val;
    let mut list: SListEntry = vec![];
    let mut i;
    for i in 0..num_entries {
        slist_prepend(&mut list, &entries[i]);
    }
    val = 0;
    assert!(slist_remove_data(&mut list, int_equal, &val) == 0);
    val = 56;
    assert!(slist_remove_data(&mut list, int_equal, &val) == 0);
    val = 8;
    assert!(slist_remove_data(&mut list, int_equal, &val) == 1);
    assert!(slist_length(&list) == num_entries - 1);
    val = 4;
    assert!(slist_remove_data(&mut list, int_equal, &val) == 4);
    assert!(slist_length(&list) == num_entries - 5);
    val = 89;
    assert!(slist_remove_data(&mut list, int_equal, &val) == 1);
    assert!(slist_length(&list) == num_entries - 6);
    slist_free(&mut list);
}
#[test]
fn test_slist_sort() {
    let entries = [89, 4, 23, 42, 4, 16, 15, 4, 8, 99, 50, 30, 4];
    let sorted = [4, 4, 4, 4, 8, 15, 16, 23, 30, 42, 50, 89, 99];
    let num_entries = entries.len();
    let mut list: Option<Box<SListEntry>> = None;
    for entry in entries.iter() {
        list = Some(slist_prepend(list, entry));
    }
    list = slist_sort(list, int_compare);
    assert_eq!(slist_length(&list), num_entries);
    for i in 0..num_entries {
        let value = slist_nth_data(&list, i);
        assert_eq!(value, sorted[i]);
    }
    slist_free(list);
    list = None;
    list = slist_sort(list, int_compare);
    assert!(list.is_none());
}
#[test]
fn test_slist_find_data() {
    let entries = [89, 23, 42, 16, 15, 4, 8, 99, 50, 30];
    let num_entries = entries.len();
    let mut list: *mut SListEntry = std::ptr::null_mut();
    let mut result: *mut SListEntry;
    let mut val: i32;
    let data: *mut i32;
    for i in 0..num_entries {
        slist_append(&mut list, &entries[i] as *const i32 as *mut i32);
    }
    for i in 0..num_entries {
        val = entries[i];
        result = slist_find_data(list, int_equal, &val as *const i32 as *mut i32);
        assert!(!result.is_null());
        data = slist_data(result) as *mut i32;
        assert!(!data.is_null());
        assert!(*data == val);
    }
    val = 0;
    assert!(slist_find_data(list, int_equal, &val as *const i32 as *mut i32).is_null());
    val = 56;
    assert!(slist_find_data(list, int_equal, &val as *const i32 as *mut i32).is_null());
    slist_free(list);
}
#[test]
fn test_slist_to_array() {
    unsafe {
        let list = generate_list();
        let array = slist_to_array(list);
        assert!(*array.offset(0) == &VARIABLE1 as *const _ as *mut _);
        assert!(*array.offset(1) == &VARIABLE2 as *const _ as *mut _);
        assert!(*array.offset(2) == &VARIABLE3 as *const _ as *mut _);
        assert!(*array.offset(3) == &VARIABLE4 as *const _ as *mut _);
        libc::free(array as *mut std::ffi::c_void);
        alloc_test_set_limit(0);
        let array = slist_to_array(list);
        assert!(array.is_null());
        slist_free(list);
    }
}
#[test]
fn test_slist_iterate() {
    let mut list: *mut SListEntry = ptr::null_mut();
    let mut iter = SListIterator {
        current: ptr::null_mut(),
        previous: ptr::null_mut(),
    };
    let mut counter: u32;
    for _ in 0..50 {
        slist_prepend(&mut list, &mut 0 as *mut _ as *mut c_void);
    }
    counter = 0;
    slist_iterate(&list, &mut iter);
    slist_iter_remove(&mut iter);
    while slist_iter_has_more(&iter) {
        slist_iter_next(&mut iter);
        counter += 1;
        if counter % 2 == 0 {
            slist_iter_remove(&mut iter);
            slist_iter_remove(&mut iter);
        }
    }
    assert!(slist_iter_next(&mut iter).is_null());
    slist_iter_remove(&mut iter);
    assert!(counter == 50);
    assert!(slist_length(list) == 25);
    slist_free(list);
    list = ptr::null_mut();
    counter = 0;
    slist_iterate(&list, &mut iter);
    while slist_iter_has_more(&iter) {
        slist_iter_next(&mut iter);
        counter += 1;
        if counter % 2 == 0 {
            slist_iter_remove(&mut iter);
        }
    }
    assert!(counter == 0);
}
#[test]
fn test_slist_iterate_bad_remove() {
    let mut list: *mut SListEntry = std::ptr::null_mut();
    let mut iter = SListIterator::default();
    let mut values = [0; 49];
    let mut val: *mut i32;
    for i in 0..49 {
        values[i] = i as i32;
        unsafe {
            slist_prepend(&mut list, &mut values[i] as *mut i32);
        }
    }
    unsafe {
        slist_iterate(&mut list, &mut iter);
        while slist_iter_has_more(&iter) {
            val = slist_iter_next(&iter) as *mut i32;
            if (*val % 2) == 0 {
                assert!(slist_remove_data(&mut list, int_equal, val as *mut _) != 0);
                slist_iter_remove(&mut iter);
            }
        }
        slist_free(list);
    }
}
fn main() {
    run_tests(tests);
    println!("num_assert: {}", num_assert);
}
